#ifndef VECTOR3_H
#define VECTOR3_H

#include "Math/Math.h"
#include "Math/Matrix3.h"
#include "Math/Vector2.h"

#include <iostream>

namespace tyro
{
	namespace Math
	{

		template <class T>
		class Vector3
		{
		public:


			/* Default contructor */
			
			Vector3<T>() : x(0), y(0), z(0){}

			/* Multi-Contructors */
			inline		Vector3<T>( T _x, T _y, T _z) : x( _x ), y( _y ), z( _z ) {}
			inline		Vector3<T>( const T* vec ) : x( vec[ 0 ] ), y( vec[ 1 ] ), z( vec[ 2 ] ) {}

			/* Operator Overloaders */

			/* Casting Operators*/
			inline 					operator T* ();
			inline 					operator const T* () const;

			/* Assignment Operators */
			inline Vector3<T>&		operator = ( const Vector3<T>& vec );
			inline Vector3<T>&		operator = ( const Vector2<T>& vec );

			/* Relational and Equality Operators */
			inline	bool			operator == ( const Vector3<T>& vec ) const;
			inline	bool			operator != ( const Vector3<T>& vec ) const;

			/* Binary Arithmetic Operators */
			inline const Vector3<T> operator - () const;
			inline const Vector3<T> operator - ( const Vector3<T>& vec ) const;
			inline const Vector3<T> operator + ( const Vector3<T>& vec ) const;
			inline const Vector3<T>	operator + () const;
			inline Vector3<T>&		operator + ( const T value );
			inline const Vector3<T> operator * ( T value ) const;
			//inline T				operator * ( const Vector3<T>& vec ) const;
			inline const Vector3<T>	operator * ( const Vector3<T>& vec ) const;
			inline const Vector3<T> operator / ( T value ) const;

			//----------------------------------------------------------------------------- 
			// Friend Scalar Multiplication
			//-----------------------------------------------------------------------------
			inline friend const Vector3<T> operator * ( T value, const Vector3<T>& vec )
			{
				return vec * value;
			}

			/* Compound Assignment Operators */
			inline	Vector3<T>&		operator += ( const Vector3<T>& vec );
			inline	Vector3<T>&		operator -= ( const Vector3<T>& vec );
			inline	Vector3<T>&		operator += ( const T value );
			inline	Vector3<T>&		operator *= ( T value );
			inline	Vector3<T>&		operator /= ( T value );

			/* Logical operators */
			inline const Vector3<T>	operator ^ ( const Vector3<T>& vec ) const;



			/* Conditional Operator */
			/* Comma Operator */
			/* Bitwise Operators */

			/* Getter and Setter */
			inline void			Set(T _x, T _y, T _z);
			inline void			Get(T& _x, T& _y, T& _z);
			inline void			Zero();
			inline T			Magnitude() const;
			inline T			MagnitudeSquared() const;
			inline T			Normalise() ;
			inline T			Dot( const Vector3<T>& vec ) const;
			inline friend T		Dot( const Vector3<T>& a, const Vector3<T>& b )
			{
				return ( a.x * b.x + a.y * b.y + a.z * b.z );
			}


			inline Vector3<T> crossProduct( const Vector3<T>& rkVector ) const
			{
				return Vector3<T>(
					y * rkVector.z - z * rkVector.y,
					z * rkVector.x - x * rkVector.z,
					x * rkVector.y - y * rkVector.x);
			}
			

			/* Vector 3 data members */
			T x;
			T y;
			T z;

			/* Constants */
			static const Vector3<T> ZERO;
			static const Vector3<T> UNIT_SCALE;
		};


		template <class T>
		inline T tyro::Math::Vector3<T>::Dot( const Vector3<T>& vec ) const
		{
			return ( x * vec.x + y * vec.y + z * vec.z );
		}

		

		template <class T>
		inline T tyro::Math::Vector3<T>::Normalise()
		{
			const T maginitude = Magnitude();
			if (maginitude == 1.0f)
			{
				x = INFINITY;
				y = INFINITY;
				z = INFINITY;
			}
			else
			{
				*this /= maginitude;
			}
			return maginitude;
		}


		//----------------------------------------------------------------------------- 
		// Casting Operator
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>::operator T* ()
		{
			return reinterpret_cast<T*>(this);
		}

		//----------------------------------------------------------------------------- 
		// Const Casting Operator
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>::operator const T* () const
		{
			return reinterpret_cast<const T*>(this);
		}

		//----------------------------------------------------------------------------- 
		// Assignment Operator
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator = ( const Vector3<T>& vec )
		{
			x = vec.x;
			y = vec.y;
			z = vec.z;
			return *this;
		}

		template <class T>
		inline Vector3<T>& Vector3<T>::operator = ( const Vector2<T>& vec )
		{
			x = vec.x;
			y = vec.y;
			z = 0.0f;
			return *this;
		}

		//----------------------------------------------------------------------------- 
		// Equal to
		//-----------------------------------------------------------------------------
		template <class T>
		inline bool Vector3<T>::operator == ( const Vector3<T>& vec ) const
		{
			return (x == vec.x && y == vec.y && z == vec.z);
		}

		//----------------------------------------------------------------------------- 
		// Not Equal to
		//-----------------------------------------------------------------------------
		template <class T>
		inline bool Vector3<T>::operator != ( const Vector3<T>& vec ) const
		{
			return (x != vec.x && y != vec.y && z != vec.z);
		}

		//----------------------------------------------------------------------------- 
		// Unary minus
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> Vector3<T>::operator - () const
		{
			return Vector3<T>( -x, -y, -z );
		}

		//----------------------------------------------------------------------------- 
		// Subtraction
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> Vector3<T>::operator - ( const Vector3<T>& vec ) const
		{
			return Vector3<T>( x - vec.x, y - vec.y, z - vec.z );
		}

		//----------------------------------------------------------------------------- 
		// Addition
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> Vector3<T>::operator + ( const Vector3<T>& vec ) const
		{
			return Vector3<T>( x + vec.x, y + vec.y, z + vec.z );
		}

		//----------------------------------------------------------------------------- 
		// Unary plus
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> Vector3<T>::operator + () const
		{
			return Vector3<T>( +x, +y, +z );
		}

		//----------------------------------------------------------------------------- 
		// Addition
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator + ( const T value )
		{
			x += value;
			y += value;
			z += value;
			return *this;
		}

		//----------------------------------------------------------------------------- 
		// Scalar Multiplication
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> Vector3<T>::operator * ( T value ) const
		{
			return Vector3<T>( x * value, y * value, z * value ); 
		}

		//----------------------------------------------------------------------------- 
		// Dot product
		//-----------------------------------------------------------------------------
		/*template <class T>
		inline T Vector3<T>::operator * ( const Vector3<T>& vec ) const
		{
			return ( x * vec.x + y * vec.y + z * vec.z ); 
		}*/

		template <class T>
		inline const Vector3<T>	Vector3<T>::operator * ( const Vector3<T>& vec ) const
		{
			return Vector3<T>(x * vec.x , y * vec.y , z * vec.z);
		}

		//----------------------------------------------------------------------------- 
		// Friend Scalar Multiplication
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> operator * ( T value, const Vector3<T>& vec )
		{
			return vec * value;
		}

		//----------------------------------------------------------------------------- 
		// Scalar Division
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T> Vector3<T>::operator / ( T value ) const
		{
			const T reciprocal = 1.0f / value;
			return Vector3<T>( x * reciprocal, y * reciprocal, z * reciprocal );
		}

		//----------------------------------------------------------------------------- 
		// Addition Assignment
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator += ( const Vector3<T>& vec )
		{
			x += vec.x;
			y += vec.y;
			z += vec.z;
			return *this;
		}

		//----------------------------------------------------------------------------- 
		// Subtraction Assignment
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator -= ( const Vector3<T>& vec )
		{
			x -= vec.x;
			y -= vec.y;
			z -= vec.z;
			return *this;
		}

		//----------------------------------------------------------------------------- 
		// Addition Assignment
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator += ( const T value )
		{
			x += value;
			y += value;
			z += value;
			return *this;
		}

		//----------------------------------------------------------------------------- 
		// Multiplication Assignment
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator *= ( T value )
		{
			x *= value;
			y *= value;
			z *= value;
			return *this;
		}
		
		//----------------------------------------------------------------------------- 
		// Division Assignment
		//-----------------------------------------------------------------------------
		template <class T>
		inline Vector3<T>& Vector3<T>::operator /= ( T value )
		{
			const T reciprocal = 1.0f / value;
			x *= reciprocal;
			y *= reciprocal;
			z *= reciprocal;
			return *this;
		}


		//-----------------------------------------------------------------------------
		// Cross Product
		//-----------------------------------------------------------------------------
		template <class T>
		inline const Vector3<T>	Vector3<T>::operator ^ ( const Vector3<T>& vec ) const
		{
			return Vector3<T>( y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x );
		}

		//----------------------------------------------------------------------------- 
		// Setter
		//-----------------------------------------------------------------------------
		template <class T>
		inline void Vector3<T>::Set(T _x, T _y, T _z)
		{
			x = _x;
			y = _y;
			z = _z;
		}

		//----------------------------------------------------------------------------- 
		// Getter
		//-----------------------------------------------------------------------------
		template <class T>
		inline void	Vector3<T>::Get(T& _x, T& _y, T& _z)
		{
			_x = x;
			_y = y;
			_z = z;
		}

		//----------------------------------------------------------------------------- 
		// Zero Vector
		//-----------------------------------------------------------------------------
		template <class T>
		inline void	Vector3<T>::Zero()
		{
			x = y = z = 0.0f;
		}

		//----------------------------------------------------------------------------- 
		// Maginitude
		//-----------------------------------------------------------------------------
		template <class T>
		inline T Vector3<T>::Magnitude() const
		{
			return sqrtf( x * x + y * y + z * z );
		}

		//----------------------------------------------------------------------------- 
		// Squared Maginitude
		//-----------------------------------------------------------------------------
		template <class T>
		inline T Vector3<T>::MagnitudeSquared() const
		{
			return ( x * x + y * y + z * z );
		}

		
	}
}
#endif